package com.jic.data.job;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.beust.jcommander.internal.Sets;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jic.common.base.vo.RestResult;
import com.jic.data.model.entity.DChannelReport;
import com.jic.data.model.entity.DFlowReport;
import com.jic.data.model.entity.DPv;
import com.jic.data.model.entity.DUserLeftReport;
import com.jic.data.service.DChannelReportService;
import com.jic.data.service.DFlowReportService;
import com.jic.data.service.DPvService;
import com.jic.data.service.DUserLeftReportService;
import com.jic.member.api.rest.MemberStatisticsApi;
import com.jic.member.request.MememberUserRequest;
import com.jic.member.response.MememberUserResponse;
import com.jic.order.api.OrderStatisticsApi;
import com.jic.order.request.OrderStatisticsRequest;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import javax.annotation.Resource;

/**
 * 名    称：ReportScheduleJob
 * 功    能：
 * 创 建 人：K.K
 * 创建时间：2020/11/28 20:24
 * 修 改 人：
 * 修改时间：
 * 说    明：
 * 版 本 号：
 */
@Configuration
@EnableScheduling
public class ReportScheduleJob {

    private static final Logger logger = LoggerFactory.getLogger(ReportScheduleJob.class);

    @Autowired
    private DPvService pvService;

    @Autowired
    private DChannelReportService channelReportService;

    @Autowired
    private DFlowReportService flowReportService;

    @Autowired
    private DUserLeftReportService userLeftReportService;

    @Resource
    private MemberStatisticsApi memberStatisticsApi;

    @Resource
    private OrderStatisticsApi orderStatisticsApi;

    /**
     * 每天凌晨2点执行一下统计
     */
    @Scheduled(cron = "0 0 2 * * ?")
    public void statistics() throws Exception {
        Date start = DateUtils.parseDate(DateFormatUtils.format(DateUtils.addDays(new Date(), -1), "yyyy-MM-dd"), new String[]{"yyyy-MM-dd"});
        Date end = DateUtils.addMilliseconds(start, 86399000);
        // 按渠道统计UID
        Map<String, Set<Long>> totalUidMap = Maps.newHashMap();
        // 按渠道统计UV
        Map<String, Set<String>> totalUvMap = Maps.newHashMap();
        // 渠道数据统计
        Map<String, DChannelReport> channelReportMap = Maps.newHashMap();
        // 流量转化统计
        Map<String, DFlowReport> flowReportMap = Maps.newHashMap();
        int pageNo = 1;
        boolean isRunning = true;
        DPv pvRequest = new DPv();
        pvRequest.setStart(start.getTime());
        pvRequest.setEnd(end.getTime());
        do {
            PageInfo<DPv> pageResult = pvService.listPageBySelective(pvRequest, pageNo++, 5000);
            if (null == pageResult || CollectionUtils.isEmpty(pageResult.getList())) {
                isRunning = false;
            } else {
                Map<String, Set<String>> batchUvMap = Maps.newHashMap();
                pageResult.getList().forEach(pv -> {
                    DChannelReport channelReport = channelReportMap.get(pv.getChannelCode());
                    if (null == channelReport) {
                        channelReport = this.initChannelReport();
                        channelReport.setChannelCode(pv.getChannelCode());
                        channelReport.setChannelName(pv.getChannelName());
                        channelReport.setCreateTime(start);
                        channelReportMap.put(pv.getChannelCode(), channelReport);
                    }
                    // PV
                    channelReport.setPv(channelReport.getPv() + 1);
                    // UV
                    if (StringUtils.isNotBlank(pv.getSessionId())) {
                        if (null == totalUvMap.get(pv.getChannelCode())) {
                            totalUvMap.put(pv.getChannelCode(), Sets.newHashSet());
                        }
                        if (totalUvMap.get(pv.getChannelCode()).add(pv.getSessionId())) {
                            // 为了分次查询新UV
                            if (null == batchUvMap.get(pv.getChannelCode())) {
                                batchUvMap.put(pv.getChannelCode(), Sets.newHashSet());
                            }
                            batchUvMap.get(pv.getChannelCode()).add(pv.getSessionId());
                        }
                        channelReport.setUv(Long.valueOf(totalUvMap.get(pv.getChannelCode()).size()));
                    }
                    // UID
                    if (null != pv.getUserId()) {
                        if (null == totalUidMap.get(pv.getChannelCode())) {
                            totalUidMap.put(pv.getChannelCode(), Sets.newHashSet());
                        }
                        totalUidMap.get(pv.getChannelCode()).add(pv.getUserId());
                    }
                });
                // 访问新用户数 = 今日新UV
                for (String channelCode : batchUvMap.keySet()) {
                    DChannelReport channelReport = channelReportMap.get(channelCode);
                    if (null != channelReport) {
                        Map<String, Object> uvQuery = Maps.newHashMap();
                        uvQuery.put("channelCode", channelCode);
                        uvQuery.put("sessionIds", batchUvMap.get(channelCode));
                        channelReport.setVisitedNewUsers(channelReport.getVisitedNewUsers() + pvService.selectNewUvCount(uvQuery));
                    }
                }
            }
        } while (isRunning);
        if (MapUtils.isNotEmpty(channelReportMap)) {
            //region #渠道数据统计
            //region #注册用户数 = 总用户数
            MememberUserRequest userRequest = new MememberUserRequest();
            Map<String, Long> userCountMap;
            try {
                userCountMap = memberStatisticsApi.queryRegisteredCount(userRequest).getData();
                if (MapUtils.isNotEmpty(userCountMap)) {
                    for (String channelCode : userCountMap.keySet()) {
                        DChannelReport channelReport = channelReportMap.get(channelCode);
                        if (null != channelReport) {
                            channelReport.setRegisteredUsers(userCountMap.get(channelCode));
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("获取注册用户数异常", e);
            }
            //endregion
            //region #访问注册用户数 = 今日注册用户数
            userRequest.setStartCreateTime(start);
            userRequest.setEndCreateTime(end);
            try {
                userCountMap = memberStatisticsApi.queryRegisteredCount(userRequest).getData();
                if (MapUtils.isNotEmpty(userCountMap)) {
                    for (String channelCode : userCountMap.keySet()) {
                        DChannelReport channelReport = channelReportMap.get(channelCode);
                        if (null != channelReport) {
                            channelReport.setRegisteredNewUsers(userCountMap.get(channelCode));
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("获取访问注册用户数异常", e);
            }
            //endregion
            //region #首次支付成功用户数
            OrderStatisticsRequest orderRequest = new OrderStatisticsRequest();
            orderRequest.setStartCreateTime(start);
            orderRequest.setEndCreateTime(end);
            Map<String, Long> orderCountMap;
            try {
                orderCountMap = orderStatisticsApi.queryFirstPaymentCount(orderRequest).getData();
                if (MapUtils.isNotEmpty(orderCountMap)) {
                    for (String channelCode : orderCountMap.keySet()) {
                        DChannelReport channelReport = channelReportMap.get(channelCode);
                        if (null != channelReport) {
                            channelReport.setFirstPaymentUsers(orderCountMap.get(channelCode));
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("首次支付成功用户数异常", e);
            }
            //endregion
            //region #提交订单用户数
            try {
                orderCountMap = orderStatisticsApi.queryOrderCount(orderRequest).getData();
                if (MapUtils.isNotEmpty(orderCountMap)) {
                    for (String channelCode : orderCountMap.keySet()) {
                        DChannelReport channelReport = channelReportMap.get(channelCode);
                        if (null != channelReport) {
                            channelReport.setSubmittedOrders(orderCountMap.get(channelCode));
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("提交订单用户数异常", e);
            }
            //endregion
            //region #支付成功订单数
            try {
                orderRequest.setOrderStatus(2);
                orderCountMap = orderStatisticsApi.queryOrderCount(orderRequest).getData();
                if (MapUtils.isNotEmpty(orderCountMap)) {
                    for (String channelCode : orderCountMap.keySet()) {
                        DChannelReport channelReport = channelReportMap.get(channelCode);
                        if (null != channelReport) {
                            channelReport.setPaymentOrders(orderCountMap.get(channelCode));
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("支付成功订单数异常", e);
            }
            //endregion
            //region #实体/虚拟商品支付成功订单数
            Map<String, Map<String, Object>> paymentOrderCountMap = null;
            try {
                paymentOrderCountMap = orderStatisticsApi.queryOrderGmv(orderRequest).getData();
            } catch (Exception e) {
                logger.error("实体/虚拟商品支付成功订单数异常", e);
            }
            //endregion
            //region #GMV
            Map<String, Map<String, Object>> orderGmvMap = null;
            try {
                orderRequest.setOrderStatus(null);
                orderGmvMap = orderStatisticsApi.queryOrderGmv(orderRequest).getData();
            } catch (Exception e) {
                logger.error("GMV异常", e);
            }
            //endregion
            //region #统计
            for (String channelCode : channelReportMap.keySet()) {
                DChannelReport channelReport = channelReportMap.get(channelCode);
                // 下单用户比例 = 提交订单用户数/访问注册用户数*100%
                channelReport.setSubmittedOrdersRate(computeRate(channelReport.getSubmittedOrders(), channelReport.getRegisteredNewUsers()));
                // 订单支付成功率 = 支付成功订单数/提交订单数*100%
                channelReport.setPaymentOrdersRate(computeRate(channelReport.getPaymentOrders(), channelReport.getSubmittedOrders()));
                // 首购转化率 = 首次支付成功用户数/访问注册用户数*100%
                channelReport.setFirstPaymentUsersRate(computeRate(channelReport.getFirstPaymentUsers(), channelReport.getRegisteredNewUsers()));
                // 总GMV
                // 实物GMV
                // 虚拟卡GMV
                if (MapUtils.isNotEmpty(orderGmvMap)) {
                    Map<String, Object> gmvMap = orderGmvMap.get(channelCode);
                    if (MapUtils.isNotEmpty(gmvMap)) {
                        channelReport.setVirtualGoodsGmv(channelReport.getVirtualGoodsGmv().add((BigDecimal) gmvMap.getOrDefault("0:TOTAL_AMOUNT", BigDecimal.ZERO)));
                        channelReport.setPhysicalGoodsGmv(channelReport.getPhysicalGoodsGmv().add((BigDecimal) gmvMap.getOrDefault("1:TOTAL_AMOUNT", BigDecimal.ZERO)));
                        channelReport.setTotalGmv(channelReport.getPhysicalGoodsGmv().add(channelReport.getVirtualGoodsGmv()));
                    }
                }
                // 实体商品支付成功订单数
                // 虚拟商品支付成功订单数
                if (MapUtils.isNotEmpty(paymentOrderCountMap)) {
                    Map<String, Object> countMap = paymentOrderCountMap.get(channelCode);
                    if (MapUtils.isNotEmpty(countMap)) {
                        channelReport.setVirtualGoodsPaymentOrders(channelReport.getVirtualGoodsPaymentOrders() + (Long) countMap.getOrDefault("0:TOTAL_COUNT", 0L));
                        channelReport.setPhysicalGoodsPaymentOrders(channelReport.getPhysicalGoodsPaymentOrders() + (Long) countMap.getOrDefault("1:TOTAL_COUNT", 0L));
                    }
                }
            }
            //endregion
            //endregion
            //region #流量转化统计
            List<Long> uidList = Lists.newArrayList();
            for (String channelCode : channelReportMap.keySet()) {
                DFlowReport flowReport = initFlowReport(channelReportMap.get(channelCode));
                flowReport.setLoginUsers(Long.valueOf(totalUidMap.get(channelCode).size()));
                flowReport.setLoginUsersCr(computeRate(flowReport.getLoginUsers(), flowReport.getUv()));
                //region #累计访问用户数
                try {
                    DFlowReport flowRequest = new DFlowReport();
                    flowRequest.setStartCreateTime(DateUtils.addDays(start, -1));
                    flowRequest.setEndCreateTime(DateUtils.addDays(end, -1));
                    PageInfo<DFlowReport> flowResult = flowReportService.listPageBySelective(flowRequest, 1, 1);
                    if (null != flowResult && CollectionUtils.isNotEmpty(flowResult.getList())) {
                        flowReport.setVisitedUsers(flowReport.getUv() + flowResult.getList().get(0).getUv());
                    }
                } catch (Exception e) {
                    logger.error("累计访问用户数异常", e);
                }
                //endregion
                //region #获取新用户UID
                try {
                    MememberUserRequest userRequest1 = new MememberUserRequest();
                    userRequest1.setStartCreateTime(start);
                    userRequest1.setEndCreateTime(end);
                    userRequest1.setIds(Lists.newArrayList(totalUidMap.get(channelCode)));
                    RestResult<List<MememberUserResponse>> userResult = memberStatisticsApi.queryRegistered(userRequest1);
                    if (null != userResult && CollectionUtils.isNotEmpty(userResult.getData())) {
                        userResult.getData().forEach(user -> uidList.add(user.getId()));
                    }
                } catch (Exception e) {
                    logger.error("获取新用户UID异常", e);
                }
                //endregion
            }

            //region #新用户提交订单数/新用户支付成功订单数
            try {
                if (CollectionUtils.isNotEmpty(uidList)) {
                    OrderStatisticsRequest orderRequest1 = new OrderStatisticsRequest();
                    orderRequest1.setStartCreateTime(start);
                    orderRequest1.setEndCreateTime(end);
                    orderRequest1.setBuyerIds(uidList);
                    Map<String, Long> orderCountMap1 = orderStatisticsApi.queryOrderCount(orderRequest1).getData();
                    if (MapUtils.isNotEmpty(orderCountMap1)) {
                        for (String channelCode : orderCountMap1.keySet()) {
                            DFlowReport flowReport = flowReportMap.get(channelCode);
                            if (null != flowReport) {
                                flowReport.setNewUserSubmittedOrders(orderCountMap1.get(channelCode));
                                flowReport.setNewUserSubmittedOrdersCr(computeRate(flowReport.getNewUserSubmittedOrders(), flowReport.getRegisteredNewUsers()));
                            }
                        }
                    }
                    // 已支付
                    orderRequest1.setOrderStatus(2);
                    orderCountMap1 = orderStatisticsApi.queryOrderCount(orderRequest1).getData();
                    if (MapUtils.isNotEmpty(orderCountMap1)) {
                        for (String channelCode : orderCountMap1.keySet()) {
                            DFlowReport flowReport = flowReportMap.get(channelCode);
                            if (null != flowReport) {
                                flowReport.setNewUserPaymentOrders(orderCountMap1.get(channelCode));
                                flowReport.setNewUserPaymentOrdersCr(computeRate(flowReport.getNewUserPaymentOrders(), flowReport.getRegisteredNewUsers()));
                            }
                        }
                    }
                }
            } catch (Exception e) {
                logger.error("新用户提交订单数/新用户支付成功订单数异常", e);
            }
            //endregion
            //endregion
            //region #批量写入报表
            channelReportMap.values().forEach(channelReport -> channelReportService.save(channelReport));
            flowReportMap.values().forEach(flowReport -> flowReportService.save(flowReport));
            //endregion
        }

        //region #用户活跃留存统计
        DPv userLeftRequest = new DPv();
        // 近30天用户留存
        userLeftRequest.setCreateDateTimestamp(DateUtils.addDays(start, -30).getTime());
        List<JSONObject> userLefts = pvService.selectUserLeft(userLeftRequest);
        if (CollectionUtils.isNotEmpty(userLefts)) {
            userLefts.forEach(userLeft -> {
                saveOrUpdateUserLeft(userLeft, 1);
            });
        }
        //endregion

        //region #用户下单留存统计
        try {
            OrderStatisticsRequest submitOrderUserLeftRequest = new OrderStatisticsRequest();
            submitOrderUserLeftRequest.setStartCreateTime(DateUtils.addDays(start, -30));
            submitOrderUserLeftRequest.setEndCreateTime(end);
            RestResult<List<JSONObject>> submitOrderUserLeftResult = orderStatisticsApi.querySubmitOrderUserLeft(submitOrderUserLeftRequest);
            if (null != submitOrderUserLeftResult && CollectionUtils.isNotEmpty(submitOrderUserLeftResult.getData())) {
                submitOrderUserLeftResult.getData().forEach(userLeft -> {
                    saveOrUpdateUserLeft(userLeft, 2);
                });
            }
        } catch (Exception e) {
            logger.error("用户下单留存统计异常", e);
        }
        //endregion
    }

    private void saveOrUpdateUserLeft(JSONObject userLeft, int type) {
        try {
            DUserLeftReport userLeftReport = initUserLeftReport(userLeft);
            userLeftReport.setType(type);
            // 判断是否已存在
            DUserLeftReport request = new DUserLeftReport();
            request.setType(type);
            request.setStart(new Date(userLeft.getLong("date")));
            request.setEnd(DateUtils.addDays(request.getStart(), 86399000));
            PageInfo<DUserLeftReport> userLeftResult = userLeftReportService.listPageBySelective(request, 1, 1);
            if (null != userLeftResult && CollectionUtils.isNotEmpty(userLeftResult.getList())) {
                userLeftReport.setId(userLeftResult.getList().get(0).getId());
            }
            userLeftReportService.saveOrUpdate(userLeftReport);
        } catch (Exception e) {
            logger.error("保存或更新用户留存异常,input=" + JSON.toJSONString(userLeft), e);
        }
    }

    private BigDecimal computeRate(Long a, Long b) {
        return BigDecimal.valueOf(a).divide(BigDecimal.valueOf(b), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
    }

    private DChannelReport initChannelReport() {
        DChannelReport channelReport = new DChannelReport();
        channelReport.setPv(0L);
        channelReport.setUv(0L);
        channelReport.setVisitedNewUsers(0L);
        channelReport.setRegisteredUsers(0L);
        channelReport.setRegisteredNewUsers(0L);
        channelReport.setFirstPaymentUsers(0L);
        channelReport.setFirstPaymentUsersRate(BigDecimal.ZERO);
        channelReport.setSubmittedOrders(0L);
        channelReport.setSubmittedOrdersRate(BigDecimal.ZERO);
        channelReport.setPaymentOrders(0L);
        channelReport.setPaymentOrdersRate(BigDecimal.ZERO);
        channelReport.setTotalGmv(BigDecimal.ZERO);
        channelReport.setPhysicalGoodsPaymentOrders(0L);
        channelReport.setPhysicalGoodsGmv(BigDecimal.ZERO);
        channelReport.setVirtualGoodsPaymentOrders(0L);
        channelReport.setVirtualGoodsGmv(BigDecimal.ZERO);
        return channelReport;
    }

    private DFlowReport initFlowReport(DChannelReport channelReport) {
        DFlowReport flowReport = new DFlowReport();
        flowReport.setChannelCode(channelReport.getChannelCode());
        flowReport.setChannelName(channelReport.getChannelName());
        flowReport.setPv(channelReport.getPv());
        flowReport.setUv(channelReport.getUv());
        flowReport.setVisitedNewUsers(channelReport.getVisitedNewUsers());
        flowReport.setVisitedUsers(0L);
        flowReport.setRegisteredUsers(channelReport.getRegisteredUsers());
        // 用户注册转化率 = 当天用户注册数/当天访问用户数*100%
        flowReport.setRegisteredUsersCr(computeRate(channelReport.getRegisteredNewUsers(), channelReport.getUv()));
        flowReport.setRegisteredNewUsers(channelReport.getRegisteredNewUsers());
        // 新用户注册转化率 = 新用户数注册数/当天访问新用户数*100%
        flowReport.setRegisteredNewUsersCr(computeRate(channelReport.getRegisteredNewUsers(), channelReport.getVisitedNewUsers()));
        flowReport.setLoginUsers(0L);
        flowReport.setLoginUsersCr(BigDecimal.ZERO);
        flowReport.setSubmittedOrders(channelReport.getSubmittedOrders());
        // 提交订单转化率 = 提交订单用户数/当天访问注册用户数*100%
        flowReport.setSubmittedOrdersCr(channelReport.getSubmittedOrdersRate());
        flowReport.setNewUserSubmittedOrders(0L);
        // 新用户提交订单转化率 = 提交订单新用户数/当天注册新用户数*100%
        flowReport.setNewUserSubmittedOrdersCr(BigDecimal.ZERO);
        flowReport.setPaymentOrders(channelReport.getPaymentOrders());
        // 支付成功转化率 = 支付成功用户数/当天访问注册用户数*100%
        flowReport.setPaymentOrdersCr(computeRate(channelReport.getPaymentOrders(), channelReport.getRegisteredNewUsers()));
        flowReport.setNewUserPaymentOrders(0L);
        // 新用户支付成功转化率 = 支付成功新用户数/当天注册新用户数*100%
        flowReport.setNewUserPaymentOrdersCr(BigDecimal.ZERO);
        flowReport.setCreateTime(channelReport.getCreateTime());
        return flowReport;
    }

    private DUserLeftReport initUserLeftReport(JSONObject source) {
        DUserLeftReport userLeftReport = JSON.parseObject(source.toJSONString(), DUserLeftReport.class);
        userLeftReport.setCreateTime(new Date(source.getLong("date")));
        userLeftReport.setUpdateTime(new Date());
        return userLeftReport;
    }
}
